﻿using System;
using System.Collections.Generic;
using CommonUtils.Logging.LoggingProviders;

namespace CommonUtils.Logging
{
    ///<summary>
    /// Generic logger
    ///</summary>
    public class Logger: IDisposable
    {
        #region Singleton pattern

        ///<summary>
        ///Private constructor
        ///</summary>
        private Logger()
        {
            LoggingProviders = new Dictionary<string, ILoggingProvider>();
        }

        /// <summary>
        /// The only instance
        /// </summary>
        private static readonly Logger instance = new Logger();

        ///<summary>
        ///The only instance
        ///</summary>
        public static Logger Instance
        {
            get
            {
                return instance;
            }
        }

        #endregion

        #region Properties

        /// <summary>
        /// List of current logging providers
        /// </summary>
        private Dictionary<string, ILoggingProvider> LoggingProviders
        {
            get;
            set;
        }

        #endregion

        #region Implementation

        /// <summary>
        /// Add logging provider
        /// </summary>
        /// <param name="loggingProvider">Logging provider</param>
        public void AddLoggingProvider(ILoggingProvider loggingProvider)
        {
            loggingProvider.InitializeLog();
            LoggingProviders.Add(loggingProvider.Name, loggingProvider);
        }

        /// <summary>
        /// Log message of specified type
        /// </summary>
        /// <param name="messageType">Message</param>
        /// <param name="message"></param>
        public void LogMessage(MessageType messageType, string message)
        {
            DateTime dateTime = DateTime.Now;

            try
            {
                foreach (ILoggingProvider loggingProvider in LoggingProviders.Values)
                {
                    loggingProvider.LogMessage(dateTime, messageType, message);
                }
            }
                //Catch and suppress all exception to prevent calling assembly workflow from any errors caused by logger
                // ReSharper disable EmptyGeneralCatchClause
            catch
            {
            }
            // ReSharper restore EmptyGeneralCatchClause
        }

        /// <summary>
        /// Check assert
        /// </summary>
        /// <param name="condition">Assert condition</param>
        public void Assert(bool condition)
        {
            Assert(condition, null);
        }

        /// <summary>
        /// Check assert
        /// </summary>
        /// <param name="condition">Assert condition</param>
        /// <param name="message">Assert message</param>
        public void Assert(bool condition, string message)
        {
            if (!condition)
            {
                LogMessage(message != null ? new FatalException(message) : new FatalException());
            }
        }

        /// <summary>
        /// Log info message
        /// </summary>
        /// <param name="message">Message</param>
        public void LogMessage(string message)
        {
            LogMessage(MessageType.Info, message);
        }

        /// <summary>
        /// Log 'output' message
        /// </summary>
        /// <param name="message">Message</param>
        public void PrintMessage(string message)
        {
            LogMessage(MessageType.Output, message);
        }

        /// <summary>
        /// Log exception as error
        /// </summary>
        /// <param name="e">Exception</param>
        public void LogMessage(Exception e)
        {
            LogMessage(MessageType.Error,
                       string.Format(
                           "{0}{5}Exception message: {1}{0}{5}Exception source: {2}{0}{5}Target site: {3}{0}{5}Stack trace: {4}",
                           Environment.NewLine,
                           e.Message,
                           e.Source,
                           e.TargetSite,
                           e.StackTrace,
                           "\t"));
            if (e.InnerException != null)
            {
                LogMessage(e.InnerException);
            }
        }

        #endregion

        #region IDisposable Members

        ///<summary>
        ///Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
        ///</summary>
        ///<filterpriority>2</filterpriority>
        public void Dispose()
        {
            foreach (ILoggingProvider loggingProvider in LoggingProviders.Values)
            {
                loggingProvider.FinalizeLog();
            }
        }

        #endregion
    }
}